//
//  ZBTableViewController.m
//  BaseRACDemo
//
//  Created by 庄波 on 2018/3/7.
//  Copyright © 2018年 庄波. All rights reserved.
//
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-property-synthesis"

#import "ZBTableViewController.h"


@interface ZBTableViewController ()
/// tableView
@property (nonatomic, readwrite, weak)   UITableView *tableView;
/// contentInset defaul is (64 , 0 , 0 , 0)
@property (nonatomic, readwrite, assign) UIEdgeInsets contentInset;
/// 视图模型
@property (nonatomic, readonly, strong) ZBTableViewModel *viewModel;

@end

@implementation ZBTableViewController

-(void)dealloc{
    _tableView.dataSource = nil;
    _tableView.delegate = nil;
}

-(instancetype)initWithViewModel:(ZBViewModel *)viewModel{
    self = [super initWithViewModel:viewModel];
    if (self) {
        if ([viewModel shouldRequestRemoteDataOnViewDidLoad]) {
            @weakify(self)
            
            [[self rac_signalForSelector:@selector(viewDidLoad)] subscribeNext:^(RACTuple * _Nullable x) {
                @strongify(self)
                /// 请求第一页的网络数据
                [self.viewModel.requestRemoteDataCommand execute:@1];
            }];
        }
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self setup];
}

/// override
- (void)bindViewModel
{
    [super bindViewModel];
    
    @weakify(self)
    //绑定datasource
    [[RACObserve(self.viewModel, dataSource) deliverOnMainThread] subscribeNext:^(id x) {
         @strongify(self)
         //deliverOnMainThread包在主线程操作UI
         // 刷新数据
         [self reloadData];
     }];
    
    /// 隐藏emptyView
    [self.viewModel.requestRemoteDataCommand.executing subscribeNext:^(NSNumber *executing) {
        @strongify(self)
        UIView *emptyDataSetView = [self.tableView.subviews.rac_sequence objectPassingTest:^(UIView *view) {
            return [NSStringFromClass(view.class) isEqualToString:@"DZNEmptyDataSetView"];
        }];
        emptyDataSetView.alpha = 1.0 - executing.floatValue;
    }];
    
    /*
     1. executionSignals:需要执行的block成功的时候返回的信号，他是在主线程执行的。
     2. executing：判断当前的block是否在执行，执行完之后会返回@(NO).
     3. enabled:当前命令是否enabled，默认是no，他也可以根据enableSignal来设置或者allowsConcurrentExecution设置为NO的时候（command已经开始执行）
     4. errors:执行command的时候获取的error都会通过这个信号发送
     5. allowsConcurrentExecution：是否允许并发执行command，默认是NO。
     */
    RACSignal *signgA = [self.viewModel.requestRemoteDataCommand.executionSignals.switchToLatest mapReplace:@YES];
    RACSignal *signgB = [self.viewModel.requestRemoteDataCommand.errors mapReplace:@NO];
    
    RAC(self,viewModel.title) = [[signgA merge:signgB] map:^id _Nullable(id  _Nullable value) {
        return [value boolValue]?self.viewModel.title:@"无网络连接";
    }];
    
    [signgA subscribeNext:^(id  _) {
        @strongify(self);
        /// 有网络
        self.viewModel.disableNetwork = NO;
    }];
    
    [signgB subscribeNext:^(id _) {
        @strongify(self);
        /// 有无网络
        self.viewModel.disableNetwork = !self.viewModel.services.client.reachabilityManager.reachable;
    }];
    
}

- (void)setup{
    // set up tableView
    /// CoderMikeHe FIXED: 纯代码布局，子类如果重新布局，建议用Masonry重新设置约束
    UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:self.viewModel.style];
    tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    // set delegate and dataSource
    tableView.delegate = self;
    tableView.dataSource = self;
    [self.view addSubview:tableView];
    
    /// 占位符
    tableView.emptyDataSetDelegate = self;
    tableView.emptyDataSetSource = self;
    [tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(UIEdgeInsetsZero);
    }];
    
    self.tableView = tableView;
    tableView.contentInset  = self.contentInset;
    // 注册cell
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"UITableViewCell"];

    /// 添加加载和刷新控件
    if (self.viewModel.shouldPullDownToRefresh) {
        /// 下拉刷新
        @weakify(self);
        [self.tableView mh_addHeaderRefresh:^(MJRefreshNormalHeader *header) {
            /// 加载下拉刷新的数据
            @strongify(self);
            [self tableViewDidTriggerHeaderRefresh];
        }];
        [self.tableView.mj_header beginRefreshing];
    }
    
    if (self.viewModel.shouldPullUpToLoadMore) {
        /// 上拉加载
        @weakify(self);
        [self.tableView mh_addFooterRefresh:^(MJRefreshAutoNormalFooter *footer) {
            /// 加载上拉刷新的数据
            @strongify(self);
            [self tableViewDidTriggerFooterRefresh];
        }];
        
        /// 隐藏footer or 无更多数据
        RAC(self.tableView.mj_footer, hidden) = [[RACObserve(self.viewModel, dataSource)
                                                  deliverOnMainThread]
                                                 map:^(NSArray *dataSource) {
                                                     @strongify(self)
                                                     NSUInteger count = dataSource.count;
                                                     /// 无数据，默认隐藏mj_footer
                                                     if (count == 0) return @1;
                                                     
                                                     if (self.viewModel.shouldEndRefreshingWithNoMoreData) return @(0);
                                                     
                                                     /// because of
                                                     return (count % self.viewModel.perPage)?@1:@0;
                                                 }];
        
    }
    
    
    if (@available(iOS 11.0, *)) {
        /// CoderMikeHe: 适配 iPhone X + iOS 11，
        AdjustsScrollViewInsetNever(self, tableView);
        /// iOS 11上发生tableView顶部有留白，原因是代码中只实现了heightForHeaderInSection方法，而没有实现viewForHeaderInSection方法。那样写是不规范的，只实现高度，而没有实现view，但代码这样写在iOS 11之前是没有问题的，iOS 11之后应该是由于开启了估算行高机制引起了bug。
        tableView.estimatedRowHeight = 0;
        tableView.estimatedSectionHeaderHeight = 0;
        tableView.estimatedSectionFooterHeight = 0;
    }
}

#pragma mark - 上下拉刷新事件
/// 下拉事件
- (void)tableViewDidTriggerHeaderRefresh{
    @weakify(self)
    [[[self.viewModel.requestRemoteDataCommand
       execute:@1]
      deliverOnMainThread]
     subscribeNext:^(id x) {
         @strongify(self)
         self.viewModel.page = 1;
         /// 重置没有更多的状态
         if (self.viewModel.shouldEndRefreshingWithNoMoreData) [self.tableView.mj_footer resetNoMoreData];
     } error:^(NSError *error) {
         @strongify(self)
         /// 已经在bindViewModel中添加了对viewModel.dataSource的变化的监听来刷新数据,所以reload = NO即可
         [self.tableView.mj_header endRefreshing];
     } completed:^{
         @strongify(self)
         /// 已经在bindViewModel中添加了对viewModel.dataSource的变化的监听来刷新数据,所以只要结束刷新即可
         [self.tableView.mj_header endRefreshing];
         /// 请求完成
         [self _requestDataCompleted];
     }];
}

/// 上拉事件
- (void)tableViewDidTriggerFooterRefresh{
    @weakify(self);
    [[[self.viewModel.requestRemoteDataCommand
       execute:@(self.viewModel.page + 1)]
      deliverOnMainThread]
     subscribeNext:^(id x) {
         @strongify(self)
         self.viewModel.page += 1;
     } error:^(NSError *error) {
         @strongify(self);
         [self.tableView.mj_footer endRefreshing];
     } completed:^{
         @strongify(self)
         [self.tableView.mj_footer endRefreshing];
         /// 请求完成
         [self _requestDataCompleted];
     }];
}

#pragma mark - sub class can override it
- (UIEdgeInsets)contentInset{
    return UIEdgeInsetsMake(SafeAreaTopHeight, 0, 0, 0);
}

/// reload tableView data
- (void)reloadData{
    [self.tableView reloadData];
}

/// duqueueReusavleCell
- (UITableViewCell *)tableView:(UITableView *)tableView dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath {
    return [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
}

/// configure cell data
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath withObject:(id)object {}


#pragma mark - 辅助方法
- (void)_requestDataCompleted{
    NSUInteger count = self.viewModel.dataSource.count;
    /// CoderMikeHe Fixed: 这里必须要等到，底部控件结束刷新后，再来设置无更多数据，否则被叠加无效
    if (self.viewModel.shouldEndRefreshingWithNoMoreData && count%self.viewModel.perPage) [self.tableView.mj_footer endRefreshingWithNoMoreData];
}


#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    
    if (self.viewModel.shouldMultiSections) return self.viewModel.dataSource ? self.viewModel.dataSource.count : 0;
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (self.viewModel.shouldMultiSections) return [self.viewModel.dataSource[section] count];
    return self.viewModel.dataSource.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [self tableView:tableView dequeueReusableCellWithIdentifier:@"UITableViewCell" forIndexPath:indexPath];
    
    // fetch object
    id object = nil;
    if (self.viewModel.shouldMultiSections) object = self.viewModel.dataSource[indexPath.section][indexPath.row];
    if (!self.viewModel.shouldMultiSections) object = self.viewModel.dataSource[indexPath.row];
    
    /// bind model
    [self configureCell:cell atIndexPath:indexPath withObject:(id)object];
    return cell;
}


#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    // execute commond
    [self.viewModel.didSelectCommand execute:indexPath];
}


#pragma mark - DZEmptyDataSetSource Methods
/*
 * 返回标题文字
 */
-(NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView{
    
    NSString *text = @"无列表数据";
    return [[NSAttributedString alloc] initWithString:text attributes:nil];
}
/*
 * 返回详细文字
 */
-(NSAttributedString *)descriptionForEmptyDataSet:(UIScrollView *)scrollView{
    NSString *text = @"哇哦～这里什么都没有发现呢？～";
    return [[NSAttributedString alloc] initWithString:text attributes:nil];
}
/*
 * 返回文字按钮
 */
//-(NSAttributedString *)buttonTitleForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state{
//    NSString *text = @"刷新";
//    return [[NSAttributedString alloc] initWithString:text attributes:nil];
//}
/*
 * 返回图片按钮
 */
-(UIImage *)buttonImageForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state{
    return kIMG_Name(@"");
}
/*
 * 自定义背景颜色
 */
-(UIColor *)backgroundColorForEmptyDataSet:(UIScrollView *)scrollView{
    return [UIColor whiteColor];
}
/*
 * 偏移量
 */

-(CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView{
    return -64.0f;
}

#pragma mark - DZMNEmptyDataSetDeleagte
/*
 * 数据源为空是否显示
 */

-(BOOL)emptyDataSetShouldDisplay:(UIScrollView *)scrollView{
    return YES;
}
/*
 * 是否允许点击
 */

-(BOOL)emptyDataSetShouldAllowTouch:(UIScrollView *)scrollView{
    return YES;
}
/*
 * 是否允许滚动
 */

-(BOOL)emptyDataSetShouldAllowScroll:(UIScrollView *)scrollView{
    return YES;
}

//处理按钮点击事件
- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button{
    
}
/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

